/*
Author: Surpass-HC
Source: https://gitee.com/hxchuan000/clpc-1
Mail: surpass-hc@qq.com
*/
#include <clptcDlgItf.h>
#include <clptcParse.h>
#include <clptcCmdList.h>
#include <queBase.h>
#include <genrand.h>
#include <clptcFunItf.h>
#include <parmNorm.h>
#include <crcCalc.h>
#if DEVICE_PLATFORM_CONFIGURE == DEVICE_PLATFORM_LINUX
#include <fileOpt.h>
#endif

int clptcDlgItfInit(clptcDlgItfStc *dlgptr, clptcFunItfStc *funptr)
{
    if (!(dlgptr && funptr))
    {
        logerrorc("");
        return -1;
    }
    dlgptr->lpFun = funptr;
    memset(dlgptr->dlgkey, 0, sizeof(dlgptr->dlgkey));
#if CLPTC_CPL_HOST_ENABLE
    sysTmBeg(dlgptr->dlgUpTmr);
    dlgptr->dlgAddr = 0xff;
    dlgptr->dlgSmSta = 0;
    dlgptr->dlgSubSta = 0;
    dlgptr->dlgDiscCtr = 0;
    dlgptr->dlgResp = 0;
    memset(dlgptr->dlgSn, 0, sizeof(dlgptr->dlgSn));
    memset(dlgptr->dlgVer, 0, sizeof(dlgptr->dlgVer));
    clptcDlgStaTransfer(dlgptr, clptcHostDlgSta_GetInf);
#endif
    return 1;
}
static int clptcDlgSubUp(clptcDlgItfStc *dlgptr)
{
    uint8_t tmpval;
    while (clptcFunItfRcv(dlgptr->lpFun, &tmpval, 1) > 0)
    {
        if (clptcSubPsAndDeal(dlgptr->lpFun->lpPkrPsr, tmpval) > 0)
        {
            if (clptcGetCmdIdV1(dlgptr->lpFun->lpPkrPsr) != clptcCmdEnum_readFileRcv)
            {
                logHexBufStr("slave resp pack", (const char *)clptcGetPackPtr(dlgptr->lpFun->lpPkrPsr),
                             clptcGetPackSizeV1(dlgptr->lpFun->lpPkrPsr));
            }
            // or send package data direct!
            clptcFunItfSnd(dlgptr->lpFun, clptcGetPackPtr(dlgptr->lpFun->lpPkrPsr), clptcGetPackSizeV1(dlgptr->lpFun->lpPkrPsr));
            // queOptWrite(dlgptr->lpFun->sndQue, clptcGetPackPtr(dlgptr->lpFun->lpPkrPsr), clptcGetPackSizeV1(dlgptr->lpFun->lpPkrPsr));
        }
    }
    return 0;
}
int clptcDlgItfUp(clptcDlgItfStc *dlgptr)
{
    if (!(dlgptr && dlgptr->lpFun))
    {
        logerrorc("");
        return -1;
    }
    if (dlgptr->lpFun->lpType != clptcFunType_Host)
    {
        return clptcDlgSubUp(dlgptr);
    }
#if CLPTC_CPL_HOST_ENABLE
    return clptcDlgHostUp(dlgptr);
#endif
    logdebugc("");
    return -1;
}

int clptcDlgMgrInit(void *dlgMgr, clptcFunItfStc *funptr, uint8_t cfgNbr)
{
    clptcDlgMgrStc *mgrptr = (clptcDlgMgrStc *)dlgMgr;
    uint8_t i;
    if (!(dlgMgr && funptr))
    {
        logerrorc("");
        return -1;
    }
    mgrptr->lpFunc = funptr;
    if (cfgNbr < 1)
    {
        logdebugc("config dialog number have to > 1");
        cfgNbr = 1;
    }
    mgrptr->dlgNbr = cfgNbr;
    for (i = 0; i < cfgNbr; i++)
    {
        clptcDlgItfInit(&mgrptr->lpDlgs[i], funptr);
    }
    return 0;
}
int clptcDlgMgrSetKey(void *dlgMgr, uint8_t cfgAddr, const uint8_t *inKey)
{
    clptcDlgMgrStc *mgrptr = (clptcDlgMgrStc *)dlgMgr;
#if CLPTC_CPL_HOST_ENABLE
    uint8_t i;
#endif
    if (!(mgrptr && mgrptr->lpFunc && inKey))
    {
        logerrorc("");
        return -1;
    }
    if (mgrptr->lpFunc->lpType != clptcFunType_Host)
    {
        cfgAddr = 0;
        memcpy(mgrptr->lpDlgs[0].dlgkey, inKey, sizeof(mgrptr->lpDlgs[0].dlgkey));
        return 1;
    }
#if CLPTC_CPL_HOST_ENABLE
    for (i = 0; i < mgrptr->dlgNbr; i++)
    {
        if (mgrptr->lpDlgs[i].dlgAddr == cfgAddr)
        {
            memcpy(mgrptr->lpDlgs[i].dlgkey, inKey, sizeof(mgrptr->lpDlgs[0].dlgkey));
            return 1;
        }
    }
#endif
    logdebugc("cfg key error");
    return -1;
}
const uint8_t *clptcDlgMgrGetKey(void *dlgMgr, uint8_t cfgAddr)
{
    clptcDlgMgrStc *mgrptr = (clptcDlgMgrStc *)dlgMgr;
#if CLPTC_CPL_HOST_ENABLE
    uint8_t i;
#endif
    if (!(mgrptr && mgrptr->lpFunc))
    {
        logerrorc("");
        return NULL;
    }
    if (mgrptr->lpFunc->lpType != clptcFunType_Host)
    {
        cfgAddr = 0;
        return mgrptr->lpDlgs[0].dlgkey;
    }
#if CLPTC_CPL_HOST_ENABLE
    for (i = 0; i < mgrptr->dlgNbr; i++)
    {
        if (mgrptr->lpDlgs[i].dlgAddr == cfgAddr)
        {
            return mgrptr->lpDlgs[i].dlgkey;
        }
    }
#endif
    logdebugc("get key error");
    return NULL;
}
clptcDlgItfStc *clptcDlgMgrGetDlg(void *dlgMgr, uint8_t cfgAddr)
{
    clptcDlgMgrStc *mgrptr = (clptcDlgMgrStc *)dlgMgr;
#if CLPTC_CPL_HOST_ENABLE
    uint8_t i;
#endif
    if (!(mgrptr && mgrptr->lpFunc))
    {
        logerrorc("");
        return NULL;
    }
    if (mgrptr->lpFunc->lpType != clptcFunType_Host)
    {
        cfgAddr = 0;
        return &mgrptr->lpDlgs[0];
    }
#if CLPTC_CPL_HOST_ENABLE
    for (i = 0; i < mgrptr->dlgNbr; i++)
    {
        if (mgrptr->lpDlgs[i].dlgAddr == cfgAddr)
        {
            return &mgrptr->lpDlgs[i];
        }
    }
#endif
    logdebugc("get key error");
    return NULL;
}
int clptcDlgMgrUpdate(void *dlgMgr)
{
    clptcDlgMgrStc *mgrptr = (clptcDlgMgrStc *)dlgMgr;
    uint8_t i;
    for (i = 0; i < mgrptr->dlgNbr; i++)
    {
        clptcDlgItfUp(&mgrptr->lpDlgs[i]);
    }
    return 0;
}
#if CLPTC_CPL_HOST_ENABLE
int clptcDlgStaTransfer(clptcDlgItfStc *dlgptr, uint8_t nextSta)
{
    sysTmBeg(dlgptr->dlgUpTmr);
    dlgptr->dlgSmSta = nextSta;
    dlgptr->dlgSubSta = 0;
    return 0;
}
int clptcDlgDoCmd(clptcDlgItfStc *dlgptr, sysTmTp timeout)
{
    sysTmTp tmptmr;
    if (clptcGetCmdIdV1(dlgptr->lpFun->lpPkrPsr) != clptcCmdEnum_wrtFileSnd)
    {
        logdebugc("dlg pack data,addr:%d,cmd:%d", dlgptr->dlgAddr, clptcGetCmdIdV1(dlgptr->lpFun->lpPkrPsr));
        logHexBufStr("host snd pack", (const char *)clptcGetPackPtr(dlgptr->lpFun->lpPkrPsr), clptcGetPackSizeV1(dlgptr->lpFun->lpPkrPsr));
    }
    clptcFunItfSnd(dlgptr->lpFun, clptcGetPackPtr(dlgptr->lpFun->lpPkrPsr), clptcGetPackSizeV1(dlgptr->lpFun->lpPkrPsr));
    // queOptWrite(dlgptr->lpFun->sndQue, (const uint8_t *)clptcGetPackPtr(dlgptr->lpFun->lpPkrPsr),
    //             clptcGetPackSizeV1(dlgptr->lpFun->lpPkrPsr));
    sysTmBeg(tmptmr);
    while (sysTmRunMs(tmptmr) < timeout)
    {
        uint8_t tmpval;
        if (clptcFunItfRcv(dlgptr->lpFun, &tmpval, 1) < 1)
        {
            usleep(10 * 1000);
            continue;
        }
        int tmpflag = clptcHostPsAndDeal(dlgptr->lpFun->lpPkrPsr, tmpval);
        if (tmpflag == 0)
        {
            continue;
        }
        if (tmpflag != 1)
        {
            logdebugc("resp error:addr=%d,resp=%d", dlgptr->dlgAddr, tmpflag);
        }
        return tmpflag;
    }
    return -1; // time out
}

int clptcDlgStaGetInf(clptcDlgItfStc *dlgptr)
{
    clptcCmdStcSnd_getInf tmpCmd = {1};
    clptcCmdStcResp_getInf *tmpResp;
    clptcCmdFuncHostPack_getInf(dlgptr->lpFun->lpPkrPsr, dlgptr->dlgAddr, clptcCrypte_None, &tmpCmd);
    if (clptcDlgDoCmd(dlgptr, 100) == 1)
    {
        dlgptr->dlgResp = 1;
        tmpResp = (clptcCmdStcResp_getInf *)clptcGetDataPtrV1(dlgptr->lpFun->lpPkrPsr);
        memcpy(dlgptr->dlgVer, tmpResp->rvers, 32);
        memcpy(dlgptr->dlgSn, tmpResp->rsn, 16);
        if (memcmp(tmpResp->rvers, CLPTC_DEVICE_VERSION_HEAD, sizeof(CLPTC_DEVICE_VERSION_HEAD) - 1) == 0)
        {
            clptcDlgStaTransfer(dlgptr, clptcHostDlgSta_SetKey);
        }
        // else if(memcmp(tmpResp->rvers, "abc2", strlen("abc2")) == 0)
        // {
        //     clptcDlgStaTransfer(dlgptr, clptcHostDlgSta_HeartNorm);
        // }
        else
        {
            return 0;
        }
    }
    else
    {
        dlgptr->dlgResp = 0;
    }
    return -1;
}
int clptcDlgStaSetKey(clptcDlgItfStc *dlgptr)
{
    clptcCmdStcSnd_setsm4key tmpCmd;
    genRandStrNorm(tmpCmd.setkey, sizeof(tmpCmd));
    clptcCmdFuncHostPack_setsm4key(dlgptr->lpFun->lpPkrPsr, dlgptr->dlgAddr, clptcCrypte_Sm4Pre, &tmpCmd);
    if (clptcDlgDoCmd(dlgptr, 200) == 1)
    {
        dlgptr->dlgDiscCtr = 0;
        // clptcDlgCmdWrtFile(dlgptr, "chkSndFile.txt", 1);
        // clptcDlgCmdReadFile(dlgptr, CLPTC_CHK_FILE_SAVE_DIR "hostRcvFile_1.txt", 1);
        clptcDlgStaTransfer(dlgptr, clptcHostDlgSta_HeartNorm);
        return 0;
    }
    logdebugc("cfg key fail");
    clptcDlgStaTransfer(dlgptr, clptcHostDlgSta_GetInf);
    return -1;
}

int clptcDlgStaHeartNormal(clptcDlgItfStc *dlgptr)
{
    clptcCmdStcSnd_heartBeat tmpheart;
    snprintf(tmpheart.hdata, sizeof(tmpheart.hdata), "host heart[%d]", dlgptr->dlgAddr);
    clptcCmdFuncHostPack_heartBeat(dlgptr->lpFun->lpPkrPsr, dlgptr->dlgAddr, clptcCrypte_Sm4Dlg, &tmpheart);
    int tmpflag = clptcDlgDoCmd(dlgptr, 200);
    // logdebugc("heart resp flag=%d", tmpflag);
    if (tmpflag == 1)
    {
        dlgptr->dlgDiscCtr = 0;
        return 0;
    }
    else if (tmpflag == -7) // sub key error.maybe sub reboot.
    {
        clptcDlgStaTransfer(dlgptr, clptcHostDlgSta_GetInf);
        return 0;
    }
    logdebugc("heart no resp:%d", dlgptr->dlgAddr);
    dlgptr->dlgDiscCtr++;
    if (dlgptr->dlgDiscCtr > 5)
    {
        clptcDlgStaTransfer(dlgptr, clptcHostDlgSta_GetInf);
    }
    return -1;
}
int clptcDlgCmdWrtFile(clptcDlgItfStc *dlgptr, const char *optFileName, uint16_t optFileId)
{
    int retflag = -1;
#if DEVICE_PLATFORM_CONFIGURE == DEVICE_PLATFORM_LINUX
    char *tmpFileBuf = (char *)malloc(64 * 1024);
    uint32_t tmpFileLen = foptRead(optFileName, tmpFileBuf, 64 * 1024);
    uint32_t tmpFileOffset;
    // uint32_t tmpFileCrc;
    uint32_t tmpFilePartLen;
    int i;
    clptcCmdStcSnd_wrtFileBeg tmpBegData;
    clptcCmdStcResp_wrtFileBeg *tmpBegRsp;
    clptcCmdStcSnd_wrtFileSnd tmpSndData;
    clptcCmdStcSnd_wrtFileFin tmpFinData;
    uint32_t tmpPackSize;
    logdebugc("host do write file:%d", tmpFileLen);

    // command send file begin.
    tmpBegData.fileId = optFileId;
    tmpBegData.fileLen = tmpFileLen;
    for (i = 0; i < 5; i++)
    {
        clptcCmdFuncHostPack_wrtFileBeg((clptcParseStc *)dlgptr->lpFun->lpPkrPsr, dlgptr->dlgAddr, clptcCrypte_None, &tmpBegData);
        if (clptcDlgDoCmd(dlgptr, 200) == 1)
        {
            break;
        }
        logdebugc("");
    }
    if (i == 8)
    {
        logdebugc("");
        retflag = -2;
        goto freePointer;
    }
    tmpBegRsp = (clptcCmdStcResp_wrtFileBeg *)clptcGetDataPtrV1(dlgptr->lpFun->lpPkrPsr);
    tmpPackSize = tmpBegRsp->packSize;
    logdebugc("sub dev pack size=%d", tmpPackSize);
    if (tmpPackSize < 16)
    {
        logdebugc("");
        retflag = -3;
        goto freePointer;
    }
    if (tmpPackSize > sizeof(tmpSndData.packData))
    {
        tmpPackSize = sizeof(tmpSndData.packData);
    }

    // command send file send.
    for (tmpFileOffset = 0; tmpFileOffset < tmpFileLen; tmpFileOffset += tmpFilePartLen)
    {
        tmpSndData.fileId = optFileId;
        tmpSndData.fileOffset = tmpFileOffset;
        tmpFilePartLen = (tmpFileLen - tmpFileOffset) > tmpPackSize ? tmpPackSize : (tmpFileLen - tmpFileOffset);
        memcpy(tmpSndData.packData, tmpFileBuf + tmpFileOffset, tmpFilePartLen);
        tmpSndData.packLen = tmpFilePartLen;
        clptcCmdFuncHostPack_wrtFileSnd((clptcParseStc *)dlgptr->lpFun->lpPkrPsr, dlgptr->dlgAddr, clptcCrypte_None, &tmpSndData);
        if (clptcDlgDoCmd(dlgptr, 200) == 1)
        {
            continue;
        }
        logdebugc("");
        retflag = -4;
        goto freePointer;
    }

    // command send file finish.
    tmpFinData.fileId = optFileId;
    tmpFinData.fileLen = tmpFileLen;
    tmpFinData.fileCrc = calcSumCrc32((const uint8_t *)tmpFileBuf, tmpFileLen);
    clptcCmdFuncHostPack_wrtFileFin((clptcParseStc *)dlgptr->lpFun->lpPkrPsr, dlgptr->dlgAddr, clptcCrypte_None, &tmpFinData);
    if (clptcDlgDoCmd(dlgptr, 200) == 1)
    {
        logdebugc("snd file finish: ok");
        retflag = 0;
    }
    else
    {
        logdebugc("snd file finish: fail");
        retflag = -5;
    }
freePointer:
    free(tmpFileBuf);
#else
#endif
    return retflag;
}

void clptcDlgCmdReadFile(clptcDlgItfStc *dlgptr, const char *optFileName, uint16_t optFileId)
{
#if DEVICE_PLATFORM_CONFIGURE == DEVICE_PLATFORM_LINUX
    char *tmpFileBuf = NULL;
    uint32_t tmpFileLen;
    uint32_t tmpFileOffset;
    uint32_t tmpFileCrc, tmpCrcHost;
    uint32_t tmpFilePartLen;
    uint32_t tmpPackSize;
    clptcCmdStcSnd_readFileBeg tmpBegData;
    clptcCmdStcResp_readFileBeg *tmpBegRsp;
    clptcCmdStcSnd_readFileRcv tmpRcvData;
    clptcCmdStcResp_readFileRcv *tmpRcvResp;
    logdebugc("host do read file:");

    foptCreate(optFileName);
    // command send file begin.
    tmpBegData.fileId = 1;
    clptcCmdFuncHostPack_readFileBeg((clptcParseStc *)dlgptr->lpFun->lpPkrPsr, dlgptr->dlgAddr, clptcCrypte_None, &tmpBegData);
    if (clptcDlgDoCmd(dlgptr, 1000) != 1)
    {
        logdebugc("");
        goto freePointer;
    }
    tmpBegRsp = (clptcCmdStcResp_readFileBeg *)clptcGetDataPtrV1(dlgptr->lpFun->lpPkrPsr);
    tmpFileLen = tmpBegRsp->fileLen;
    tmpFileCrc = tmpBegRsp->fileCrc;
    tmpPackSize = tmpBegRsp->packSize;
    if (tmpPackSize < 16)
    {
        logdebugc("");
        goto freePointer;
    }
    if (tmpPackSize > (sizeof(clptcCmdStcResp_readFileRcv) - offsetof(clptcCmdStcResp_readFileRcv, packData)))
    {
        tmpPackSize = sizeof(clptcCmdStcResp_readFileRcv) - offsetof(clptcCmdStcResp_readFileRcv, packData);
    }
    logdebugc("get sub file info:%d,%d,%d", tmpFileLen, tmpFileCrc, tmpPackSize);

    // command send file send.
    for (tmpFileOffset = 0; tmpFileOffset < tmpFileLen; tmpFileOffset += tmpFilePartLen)
    {
        tmpRcvData.fileId = 1;
        tmpRcvData.fileOffset = tmpFileOffset;
        tmpFilePartLen = (tmpFileLen - tmpFileOffset) > tmpPackSize ? tmpPackSize : (tmpFileLen - tmpFileOffset);
        tmpRcvData.packLen = tmpFilePartLen;
        clptcCmdFuncHostPack_readFileRcv((clptcParseStc *)dlgptr->lpFun->lpPkrPsr, dlgptr->dlgAddr, clptcCrypte_None, &tmpRcvData);
        if (clptcDlgDoCmd(dlgptr, 200) != 1)
        {
            logdebugc("");
            goto freePointer;
        }
        tmpRcvResp = (clptcCmdStcResp_readFileRcv *)clptcGetDataPtrV1(dlgptr->lpFun->lpPkrPsr);
        fOffsetWrite(optFileName, tmpFileOffset, tmpRcvResp->packData, tmpFilePartLen);
    }

    tmpFileBuf = malloc(tmpFileLen);
    if (!tmpFileBuf)
    {
        logdebugc("");
        goto freePointer;
    }
    foptRead(optFileName, tmpFileBuf, tmpFileLen);
    tmpCrcHost = calcSumCrc32((const uint8_t *)tmpFileBuf, tmpFileLen);
    if (tmpCrcHost != tmpFileCrc)
    {
        logdebugc("");
        goto freePointer;
    }
    logdebugc("read file ok");
freePointer:
    if (tmpFileBuf)
    {
        free(tmpFileBuf);
        tmpFileBuf = NULL;
    }
#else
#endif
    return;
}
int clptcDlgHostUp(clptcDlgItfStc *dlgptr)
{
    if (dlgptr->dlgAddr == 0xff)
    {
        return 0;
    }
    switch (dlgptr->dlgSmSta)
    {
    case clptcHostDlgSta_GetInf:
    {
        if (sysTmRunMs(dlgptr->dlgUpTmr) < 5000)
        {
            break;
        }
        sysTmBeg(dlgptr->dlgUpTmr);
        clptcDlgStaGetInf(dlgptr);
        break;
    }
    case clptcHostDlgSta_SetKey:
    {
        clptcDlgStaSetKey(dlgptr);
        break;
    }
    case clptcHostDlgSta_HeartNorm:
    {
        if (sysTmRunMs(dlgptr->dlgUpTmr) < 5000)
        {
            break;
        }
        sysTmBeg(dlgptr->dlgUpTmr);
        clptcDlgStaHeartNormal(dlgptr);
        break;
    }
    default:
        break;
    }
    return 0;
}
int clptcDlgMgrSetAddrs(void *dlgMgr, const uint8_t *inAddrs, uint8_t inAddrLen)
{
    clptcDlgMgrStc *mgrptr = (clptcDlgMgrStc *)dlgMgr;
    uint8_t i, j, tmpfind, tmpid;
    if (!(mgrptr && mgrptr->lpFunc && inAddrs))
    {
        logerrorc("");
        return -1;
    }
    if (mgrptr->lpFunc->lpType == clptcFunType_Sub)
    {
        // sub dlg do not set addr!
        logdebugc("");
        return 1;
    }
    for (i = 0; i < inAddrLen; i++)
    {
        tmpid = 0xff;
        tmpfind = 0;
        if (inAddrs[i] == 0xff)
        {
            logerrorc("");
            continue;
        }
        // add address if dialog buf not exist.
        for (j = 0; j < mgrptr->dlgNbr; j++)
        {
            if (mgrptr->lpDlgs[j].dlgAddr == 0xff && tmpid == 0xff)
            {
                tmpid = j;
            }
            else if (mgrptr->lpDlgs[j].dlgAddr == inAddrs[i])
            {
                tmpfind = 1;
                break;
            }
        }
        if (tmpfind)
        {
            continue;
        }
        if (tmpid != 0Xff)
        {
            logdebugc("add addr:%d", inAddrs[i]);
            mgrptr->lpDlgs[tmpid].dlgAddr = inAddrs[i];
            clptcDlgStaTransfer(&mgrptr->lpDlgs[tmpid], clptcHostDlgSta_GetInf);
        }
    }
    // delete address if dialog buf more.
    for (j = 0; j < mgrptr->dlgNbr; j++)
    {
        tmpfind = 0;
        for (i = 0; i < inAddrLen; i++)
        {
            if (mgrptr->lpDlgs[j].dlgAddr == inAddrs[i])
            {
                tmpfind = 1;
                break;
            }
        }
        if (tmpfind)
        {
            continue;
        }
        if (mgrptr->lpDlgs[j].dlgAddr == 0xff)
        {
            continue;
        }
        logdebugc("delete addr:%d", mgrptr->lpDlgs[j].dlgAddr);
        mgrptr->lpDlgs[j].dlgAddr = 0xff;
    }
    return 1;
}
#endif
